package com.zxb.base.generator.service.impl;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zxb.base.generator.config.EngineConfig;
import com.zxb.base.generator.config.EngineConstant;
import com.zxb.base.generator.enm.ColumnTypeEnum;
import com.zxb.base.generator.service.DdlService;
import com.zxb.base.generator.service.EngineService;
import com.zxb.base.generator.util.FreeMarkerUtil;
import com.zxb.base.generator.util.NameUtil;
import com.zxb.base.generator.vo.*;
import com.zxb.core.dao.generator.GenColumn;
import com.zxb.core.dao.generator.GenColumnDao;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author Zxb
 */
@Service
@AllArgsConstructor
@Slf4j
public class EngineServiceImpl implements EngineService {
    private GenColumnDao columnDao;

    private DdlService ddlService;

    @Override
    public List<GenColumn> findGenColumnByTable(String tableName) {
        return columnDao.list(
                new LambdaQueryWrapper<GenColumn>()
                        .eq(GenColumn::getTableName, tableName)
                        .orderByAsc(GenColumn::getId));
    }

    @Override
    public GenColumn findGenColumnByTable(String tableName, String columnName) {
        return columnDao.getOne(
                new LambdaQueryWrapper<GenColumn>()
                        .eq(GenColumn::getTableName, tableName)
                        .eq(GenColumn::getColumnName, columnName)
                        .orderByAsc(GenColumn::getId));
    }

    @Override
    public void generateEntity(String schema, String tableName) {
        EntityVo entity = new EntityVo();
        entity.setPackageName(EngineConfig.packageName.get());
        entity.setHasFk(false);
        String className = NameUtil.genClassName(tableName);
        entity.setName(className);

        List<ColumnVo> colList = ddlService.findColumnByTable(tableName, schema);
        Set<String> proTypeSet = new LinkedHashSet<>();
        List<PropertyVo> proList = new ArrayList<>();
        for (ColumnVo col : colList) {

            String columnName = col.getName();
            if (isPubColumn(columnName)) {
                continue;
            }
            Class<?> type = ColumnTypeEnum.oracle2Java(col.getType());
            PropertyVo pro = new PropertyVo();
            pro.setName(NameUtil.genProName(columnName));
            pro.setColumnVo(col);
            pro.setStaticName(NameUtil.genStaticProName(columnName));
            pro.setType(type);
            pro.setShortTypeName(type.getSimpleName());

            GenColumn genColumn = findGenColumnByTable(tableName, columnName);
            String referClass = genColumn.getReferTableName();
            String referName = genColumn.getReferColumnName();
            if (StrUtil.isNotBlank(referClass)
                    && StrUtil.isNotBlank(referName)) {
                pro.setIsFk(true);
                if (!entity.getHasFk()) {
                    entity.setHasFk(true);
                }
                pro.setPkClassName(referClass);
                pro.setName(referName);
                pro.setStaticName(StrUtil.toUnderlineCase(referName).toUpperCase());
            } else {
                pro.setIsFk(false);
                proTypeSet.add(type.getName());
            }
            proList.add(pro);
        }
        proList.sort((p1, p2) -> {
            if (p1.getIsFk() && !p2.getIsFk()) {
                return 1;
            }
            if (!p1.getIsFk() && p2.getIsFk()) {
                return -1;
            }
            return 0;
        });
        entity.setProList(proList);
        entity.setProTypeSet(proTypeSet);
        FreeMarkerUtil.generate(entity, "entity.ftl", className + ".java");
    }

    private ControllerVo buildController(String schema, String tableName) {
        String name = NameUtil.genClassName(tableName);
        String mapping = NameUtil.genMapping(tableName);
        String entityImport = NameUtil.genEntityImport(tableName);
        List<String> editProNames = new ArrayList<>();
        List<String> batchProNames = new ArrayList<>();
        List<ColumnVo> colList = ddlService.findColumnByTable(tableName, schema);
        colList = colList.stream().filter(col -> !isPubColumn(col.getName())).collect(Collectors.toList());
        for (ColumnVo col : colList) {
            GenColumn sg = columnDao.getOne(
                    new LambdaQueryWrapper<GenColumn>()
                            .eq(GenColumn::getTableName, tableName)
                            .eq(GenColumn::getColumnName, col.getName()));
            if (sg != null && StrUtil.isEmpty(sg.getReferColumnName())) {
                if (sg.getBatchShow()) {
                    batchProNames.add(NameUtil.genProName(col.getName()));
                }
                if (sg.getEditShow()) {
                    editProNames.add(NameUtil.genProName(col.getName()));
                }
            }
        }

        ControllerVo c = new ControllerVo();
        c.setPackageName(EngineConfig.packageName.get());
        c.setName(name);
        c.setMapping(mapping);
        c.setEntityImport(entityImport);
        c.setBatchProNames(batchProNames);
        c.setEditProNames(editProNames);
        return c;
    }

    @Override
    public void generateController(String schema, String tableName) {
        ControllerVo c = buildController(schema, tableName);
        FreeMarkerUtil.generate(c, "controller.ftl", c.getName() + "Controller.java");
    }

    @Override
    public void generateService(String schema, String tableName) {
        ControllerVo c = buildController(schema, tableName);
        FreeMarkerUtil.generate(c, "service.ftl", c.getName() + "Service.java");
    }

    @Override
    public void generateServiceImpl(String schema, String tableName) {
        ControllerVo c = buildController(schema, tableName);
        FreeMarkerUtil.generate(c, "service.impl.ftl", c.getName() + "ServiceImpl.java");
    }

    @Override
    public void generateChildController(String schema, String tableName, String parentTableName) {
        ControllerVo c = buildController(schema, tableName);
        //子表
        c.setParent(NameUtil.genClassName(parentTableName));
        c.setParentStaticName(NameUtil.genStaticProName(parentTableName));
        c.setParentEntityImport(NameUtil.genEntityImport(parentTableName));
        FreeMarkerUtil.generate(c, "controller.child.ftl", c.getName() + "Controller.java");
    }

    @Override
    public void generateTreeJava(String tableName) {
        String name = NameUtil.genClassName(tableName);
        String mapping = NameUtil.genMapping(tableName);
        String entityImport = NameUtil.genEntityImport(tableName);
        ControllerVo c = new ControllerVo();
        c.setName(name);
        c.setMapping(mapping);
        c.setEntityImport(entityImport);
        FreeMarkerUtil.generate(c, "tree.controller.ftl", name + "Controller.java");
        FreeMarkerUtil.generate(c, "tree.service.ftl", name + "Service.java");
        FreeMarkerUtil.generate(c, "tree.service.impl.ftl", name + "ServiceImpl.java");
    }

    private VueVo buildVue(String tableName) {
        List<GenColumn> list = findGenColumnByTable(tableName);
        list = list.stream().filter(t -> !isPubColumn(t.getColumnName())).collect(Collectors.toList());
        List<PropertyVo> proList = new ArrayList<>();
        for (GenColumn gc : list) {
            PropertyVo pro = new PropertyVo();
            pro.setColumnVo(new ColumnVo(gc.getColumnName(), gc.getColumnType(), gc.getFormLabel()));
            pro.setName(NameUtil.genProName(gc.getColumnName()));
            pro.setFormType(gc.getCompType());
            pro.setListShow(gc.getListShow());
            pro.setEditShow(gc.getEditShow());
            pro.setInfoShow(gc.getInfoShow());
            pro.setBatchShow(gc.getBatchShow());
            proList.add(pro);
        }

        VueVo vue = new VueVo();
        vue.setBaseMapping(NameUtil.genMapping(tableName));
        vue.setDescription(tableName);
        vue.setProList(proList);
        return vue;
    }

    @Override
    public void generateVue(String schema, String tableName) {
        VueVo vue = buildVue(tableName);
        FreeMarkerUtil.generate(vue, "vue.Add.ftl", "Add.vue");
        FreeMarkerUtil.generate(vue, "vue.Update.ftl", "Update.vue");
        FreeMarkerUtil.generate(vue, "vue.Batch.ftl", "Batch.vue");
        FreeMarkerUtil.generate(vue, "vue.Info.ftl", "Info.vue");
        FreeMarkerUtil.generate(vue, "vue.Index.ftl", "Index.vue");
        FreeMarkerUtil.generate(vue, "vue.dic.js.ftl", "dic.js");
    }

    @Override
    public void generateChildVue(String schema, String tableName) {
        VueVo vue = buildVue(tableName);
        FreeMarkerUtil.generate(vue, "vue.Add.ftl", "Add.vue");
        FreeMarkerUtil.generate(vue, "vue.Update.ftl", "Update.vue");
        FreeMarkerUtil.generate(vue, "vue.Batch.ftl", "Batch.vue");
        FreeMarkerUtil.generate(vue, "vue.Info.ftl", "Info.vue");
        FreeMarkerUtil.generate(vue, "vue.Index.child.ftl", "Index.vue");
        FreeMarkerUtil.generate(vue, "vue.dic.js.ftl", "dic.js");
    }

    @Override
    public void generateTreeVue(String schema, String tableName) {
        VueVo vue = new VueVo();
        vue.setBaseMapping(NameUtil.genMapping(tableName));
        FreeMarkerUtil.generate(vue, "vue.tree.left.ftl", "Left.vue");
        FreeMarkerUtil.generate(vue, "vue.tree.index.ftl", "Index.vue");
    }

    @Override
    public Boolean isPubColumn(String name) {
        return EngineConstant.PUB_COLUMNS.contains(name);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<GenColumn> initTable(String schema, String tableName, List<ColumnVo> colList) {
        columnDao.remove(
                new LambdaQueryWrapper<GenColumn>()
                        .eq(GenColumn::getTableName, tableName));
        List<GenColumn> list = new ArrayList<>();
        for (ColumnVo col : colList) {
            if (EngineConstant.PUB_COLUMNS.contains(col.getName())) {
                continue;
            }
            GenColumn column = new GenColumn();
            column.setTableName(tableName);
            column.setColumnName(col.getName());
            String type = col.getType();
            column.setColumnType(type);
            column.setCompType("text");
            switch (type) {
                case "bigint":
                case "int":
                    column.setCompType("int");
                    break;
                case "double":
                    column.setCompType("double");
                    break;
                case "float":
                    column.setCompType("float");
                    break;
                case "tinyint":
                    column.setCompType("switch");
                    break;
                case "text":
                    column.setCompType("tinymce");
                    break;
                case "date":
                    column.setCompType("date");
                    break;
                case "time":
                    column.setCompType("time");
                    break;
                default:
            }
            column.setFormLabel(col.getComment());
            column.setReferTableName("");
            column.setListShow(true);
            column.setEditShow(true);
            column.setInfoShow(true);
            column.setBatchShow(true);
            list.add(column);
        }
        columnDao.saveBatch(list);
        return list;
    }
}
